package main

import (
	"context"
	// "errors"
	"fmt"
	"io/ioutil"
	"strconv"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/common/crypto"
	"github.com/hyperledger/fabric/core/comm"
	"github.com/hyperledger/fabric/peer/chaincode"
	"github.com/hyperledger/fabric/peer/common"
	"github.com/hyperledger/fabric/peer/common/api"
	cb "github.com/hyperledger/fabric/protos/common"
	common2 "github.com/hyperledger/fabric/protos/common"
	ab "github.com/hyperledger/fabric/protos/orderer"
	pb "github.com/hyperledger/fabric/protos/peer"
	"github.com/hyperledger/fabric/protos/utils"
	// "google.golang.org/grpc"
)

func say() {
	fmt.Println("hello, world")
}

var (
	address            = "peer0.org1.example.com:7051"
	serverNameOverride = ""
)

func main() {
	timestemp := strconv.FormatInt(time.Now().Unix(), 10)
	fmt.Println(timestemp)
	fmt.Println(len(timestemp))

	// GetNodeStatus()
	// GetState("abc")
	// PutState()
	// GetState("abc")
}

// ./peer chaincode invoke -C mychannel -n storage -c '{"Args":["PutState","abc","newabc"]}'
func PutState() {
	cf, err := InitCmdFactory2()
	if err != nil {
		fmt.Println(err)
		return
	}
	spec, err := getChaincodeSpec2()
	if err != nil {
		fmt.Println(err)
		return
	}

	txID := ""

	proposalResp, err := chaincode.ChaincodeInvokeOrQuery(
		spec,
		"mychannel",
		txID,
		true,
		cf.Signer,
		cf.Certificate,
		cf.EndorserClients,
		cf.DeliverClients,
		cf.BroadcastClient)

	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(proposalResp.Response.Payload)

}

func getChaincodeSpec2() (*pb.ChaincodeSpec, error) {
	spec := &pb.ChaincodeSpec{}
	// if err := checkChaincodeCmdParams(cmd); err != nil {
	// 	// unset usage silence because it's a command line usage error
	// 	cmd.SilenceUsage = false
	// 	return spec, err
	// }

	// Build the spec
	input := &pb.ChaincodeInput{}
	input.Args = [][]byte{[]byte("PutState"), []byte("abc"), []byte("456")}
	// if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
	// 	return spec, errors.Wrap(err, "chaincode argument error")
	// }

	chaincodePath := ""
	chaincodeName := "storage"
	chaincodeVersion := ""
	chaincodeLang := "GOLANG"
	spec = &pb.ChaincodeSpec{
		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
		ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
		Input:       input,
	}
	return spec, nil
}

func newGRPCClient2() (gclient *comm.GRPCClient, err error) {
	clientConfig := comm.ClientConfig{}
	clientConfig.Timeout = time.Second * 3

	secOpts := &comm.SecureOptions{
		UseTLS:            true,
		RequireClientCert: false,
	}

	if secOpts.UseTLS {
		caPEM, res := ioutil.ReadFile("/Users/yangyf/godev/gitee.com/kobeyyf/nothing/fabric-sdk/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem")
		if res != nil {
			return nil, res
		}
		secOpts.ServerRootCAs = [][]byte{caPEM}
	}
	clientConfig.SecOpts = secOpts

	return comm.NewGRPCClient(clientConfig)

}

func InitCmdFactory2() (ch *chaincode.ChaincodeCmdFactory, err error) {
	gCleint, err := newGRPCClient()
	if err != nil {
		return nil, err
	}
	conn, err := gCleint.NewConnection(address, serverNameOverride)
	if err != nil {
		return nil, err
	}

	eClient := pb.NewEndorserClient(conn)
	pbClient := pb.NewDeliverClient(conn)
	dClient := common.PeerDeliverClient{Client: pbClient}

	certFunc := gCleint.Certificate()

	signer, err := common.GetDefaultSignerFnc()
	if err != nil {
		return nil, err
	}

	gClient, err := newGRPCClient2()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	gConn, err := gClient.NewConnection("orderer.example.com:7050", "")
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	bClient, err := ab.NewAtomicBroadcastClient(gConn).Broadcast(context.TODO())
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	return &chaincode.ChaincodeCmdFactory{
		EndorserClients: []pb.EndorserClient{eClient},
		DeliverClients:  []api.PeerDeliverClient{dClient},
		Signer:          signer,
		BroadcastClient: &broadcastClient{client: bClient},
		Certificate:     certFunc,
	}, nil
}

func getChaincodeSpec() (*pb.ChaincodeSpec, error) {
	spec := &pb.ChaincodeSpec{}
	// if err := checkChaincodeCmdParams(cmd); err != nil {
	// 	// unset usage silence because it's a command line usage error
	// 	cmd.SilenceUsage = false
	// 	return spec, err
	// }

	// Build the spec
	input := &pb.ChaincodeInput{}
	input.Args = [][]byte{[]byte("GetState"), []byte("abc")}
	// if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
	// 	return spec, errors.Wrap(err, "chaincode argument error")
	// }

	chaincodePath := ""
	chaincodeName := "storage"
	chaincodeVersion := ""
	chaincodeLang := "GOLANG"
	spec = &pb.ChaincodeSpec{
		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
		ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
		Input:       input,
	}
	return spec, nil
}

// ./peer chaincode query -C mychannel -n storage -c '{"Args":["GetState","abc"]}'
func GetState(key string) {
	cf, err := InitCmdFactory()
	if err != nil {
		fmt.Println(err)
		return
	}
	spec, err := getChaincodeSpec()
	if err != nil {
		fmt.Println(err)
		return
	}
	txID := ""

	proposalResp, err := chaincode.ChaincodeInvokeOrQuery(
		spec,
		"mychannel",
		txID,
		false,
		cf.Signer,
		cf.Certificate,
		cf.EndorserClients,
		cf.DeliverClients,
		cf.BroadcastClient)

	if err != nil {
		fmt.Println(err)
		return
	}

	t := proposalResp.GetTimestamp()
	fmt.Println(t.String())
	fmt.Println(string(proposalResp.Endorsement.GetEndorser()))
	fmt.Println(proposalResp.GetResponse().String())
	fmt.Println(string(proposalResp.GetPayload()))
	fmt.Println(proposalResp.Response.Payload)
}

func InitCmdFactory() (ch *chaincode.ChaincodeCmdFactory, err error) {
	gCleint, err := newGRPCClient()
	if err != nil {
		return nil, err
	}
	conn, err := gCleint.NewConnection(address, serverNameOverride)
	if err != nil {
		return nil, err
	}

	eClient := pb.NewEndorserClient(conn)
	pbClient := pb.NewDeliverClient(conn)
	dClient := common.PeerDeliverClient{Client: pbClient}

	certFunc := gCleint.Certificate()

	signer, err := common.GetDefaultSignerFnc()
	if err != nil {
		return nil, err
	}
	return &chaincode.ChaincodeCmdFactory{
		EndorserClients: []pb.EndorserClient{eClient},
		DeliverClients:  []api.PeerDeliverClient{dClient},
		Signer:          signer,
		BroadcastClient: nil,
		Certificate:     certFunc,
	}, nil
}

func newGRPCClient() (gclient *comm.GRPCClient, err error) {

	mspMgrConfigDir := "/Users/yangyf/Desktop/dev/godev/src/gitee.com/kobeyyf/nothing/fabric-sdk/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"
	localMSPID := "Org1MSP"
	localMSPType := "bccsp"

	common.InitCrypto(mspMgrConfigDir, localMSPID, localMSPType)

	clientConfig := comm.ClientConfig{}
	clientConfig.Timeout = time.Second * 3

	secOpts := &comm.SecureOptions{
		UseTLS:            true,
		RequireClientCert: false,
	}

	if secOpts.UseTLS {
		caPEM, res := ioutil.ReadFile("/Users/yangyf/godev/gitee.com/kobeyyf/nothing/fabric-sdk/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt")
		if res != nil {
			return nil, res
		}
		secOpts.ServerRootCAs = [][]byte{caPEM}
	}
	clientConfig.SecOpts = secOpts

	return comm.NewGRPCClient(clientConfig)
}

// func newClient2() {
// 	address := "peer0.org1.example.com:7051"
// 	tlsRootCertFile := "/Users/yangyf/godev/gitee.com/kobeyyf/nothing/fabric-sdk/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt"
// 	client, err := common.GetEndorserClientFnc(address, tlsRootCertFile)
// 	if err != nil {
// 		fmt.Println(err)
// 		return
// 	}

// }

func GetNodeStatus() {
	gClient, err := newGRPCClient()
	if err != nil {
		fmt.Println(err)
		return
	}
	conn, err := gClient.NewConnection(address, serverNameOverride)

	adminClient := pb.NewAdminClient(conn)

	signer, err := common.GetDefaultSigner()
	if err != nil {
		fmt.Println(err)
		return
	}
	localSigner := crypto.NewSignatureHeaderCreator(signer)

	wrapEnv := func(msg proto.Message) *common2.Envelope {
		env, err := utils.CreateSignedEnvelope(common2.HeaderType_PEER_ADMIN_OPERATION, "", localSigner, msg, 0, 0)
		if err != nil {
			fmt.Println(err)
		}
		return env
	}

	status, err := adminClient.GetStatus(context.Background(), wrapEnv(&pb.AdminOperation{}))
	if err != nil {
		fmt.Println(&pb.ServerStatus{Status: pb.ServerStatus_UNKNOWN})
		return
	}
	fmt.Println(status)

	return
}

type broadcastClient struct {
	client ab.AtomicBroadcast_BroadcastClient
}

// // GetBroadcastClient creates a simple instance of the BroadcastClient interface
// func GetBroadcastClient() (BroadcastClient, error) {
// 	oc, err := NewOrdererClientFromEnv()
// 	if err != nil {
// 		return nil, err
// 	}
// 	bc, err := oc.Broadcast()
// 	if err != nil {
// 		return nil, err
// 	}

// 	return &broadcastClient{client: bc}, nil
// }

func (s *broadcastClient) getAck() error {
	msg, err := s.client.Recv()
	if err != nil {
		return err
	}
	if msg.Status != cb.Status_SUCCESS {
		return err
		// return errors.Errorf("got unexpected status: %v -- %s", msg.Status, msg.Info)
	}
	return nil
}

//Send data to orderer
func (s *broadcastClient) Send(env *cb.Envelope) error {
	if err := s.client.Send(env); err != nil {
		return err
		// return errors.WithMessage(err, "could not send")
	}

	err := s.getAck()

	return err
}

func (s *broadcastClient) Close() error {
	return s.client.CloseSend()
}

// func newPeerClientForClientConfig(address, override string, clientConfig comm.ClientConfig) (*common.PeerClient, error) {
// 	gClient, err := comm.NewGRPCClient(clientConfig)
// 	if err != nil {
// 		return nil, errors.WithMessage(err, "failed to create PeerClient from config")
// 	}
// 	pClient := &PeerClient{
// 		commonClient: commonClient{
// 			GRPCClient: gClient,
// 			address:    address,
// 			sn:         override}}
// 	return pClient, nil
// }
